package org.zebra.mybatis.plus.core;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.base.MPJBaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.zebra.mybatis.plus.core.query.QueryHelper;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * serviceImpl 增强
 *
 * @param <M> mapper类型
 * @param <T> 实体类型
 * @author zhanghongbin
 */
@SuppressWarnings("unchecked")
@Slf4j
public class IServicePlus<M extends MapperPlus<T>, T> extends MPJBaseServiceImpl<M, T> {

    @Override
    protected Class<M> currentMapperClass() {
        return null;
    }

    @Override
    protected Class<T> currentModelClass() {
        return null;
    }

    public <DTO, Q> DTO findOne(Q query, Class<DTO> dtoClass) {
        QueryWrapper<T> queryWrapper = QueryHelper.build(query);
        return BeanUtil.toBean(this.getOne(queryWrapper), dtoClass);
    }

    public <DTO, Q> List<DTO> findList(Q query, Class<DTO> dtoClass) {
        QueryWrapper<T> queryWrapper = QueryHelper.build(query);
        return BeanUtil.copyToList(this.list(queryWrapper), dtoClass);
    }

    public <DTO, Q extends MPageQuery> Page<DTO> findPage(Q query, Class<DTO> dtoClass) {
        QueryWrapper<T> queryWrapper = QueryHelper.build(query);
        Page page = this.page(query.build(), queryWrapper);
        page.setRecords(BeanUtil.copyToList(page.getRecords(), dtoClass));
        return page;
    }

    public final <DTO, P extends IPage<DTO>> P findPage(IPage page, Wrapper<T> wrapper, Class<DTO> dtoClass) {
        List list = this.getBaseMapper().selectList(page, wrapper);
        // 创建一个新的VO对象分页列表，并设置分页信息
        // IPage<C> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        if (!CollUtil.isEmpty(list)) {
            page.setRecords(BeanUtil.copyToList(list, dtoClass));
        }
        return (P) page;
    }

    public <DTO, P> List<DTO> findListBySqlPlus(Class<DTO> dtoClass, String sql, P params) {
        if (params instanceof Map) {
            return BeanUtil.copyToList(this.getBaseMapper().selectListBySqlPlus(sql, (Map) params), dtoClass);
        } else {
            return BeanUtil.copyToList(
                    this.getBaseMapper().selectListBySqlPlus(sql, BeanUtil.beanToMap(params)), dtoClass);
        }
    }

    public <DTO, P> DTO findOneBySqlPlus(Class<DTO> dtoClass, String sql, P params) {
        Map<String, Object> result;
        if (params instanceof Map) {
            result = this.getBaseMapper().selectOneBySqlPlus(sql, (Map) params);
        } else {
            result = this.getBaseMapper().selectOneBySqlPlus(sql, BeanUtil.beanToMap(params));
        }
        return BeanUtil.toBean(result, dtoClass);
    }

    public <DTO, P> Page<DTO> findPageBySqlPlus(Class<DTO> dtoClass, IPage page, String sql, P params) {
        Page pageResult;
        if (params instanceof Map) {
            pageResult = this.getBaseMapper().selectPageBySqlPlus(page, sql, (Map) params);
        } else {
            pageResult = this.getBaseMapper().selectPageBySqlPlus(page, sql, BeanUtil.beanToMap(params));
        }
        pageResult.setRecords(BeanUtil.copyToList(pageResult.getRecords(), dtoClass));
        return pageResult;
    }

    public <P> int updateBySqlPlus(String sql, P params) {
        if (params instanceof Map) {
            return this.getBaseMapper().updateBySqlPlus(sql, (Map) params);
        } else {
            return this.getBaseMapper().updateBySqlPlus(sql, BeanUtil.beanToMap(params));
        }
    }

    public <P> int deleteBySqlPlus(String sql, P params) {
        if (params instanceof Map) {
            return this.getBaseMapper().deleteBySqlPlus(sql, (Map) params);
        } else {
            return this.getBaseMapper().deleteBySqlPlus(sql, BeanUtil.beanToMap(params));
        }
    }

    public <Q> boolean deleteByQuery(Q query) {
        QueryWrapper<T> queryWrapper = QueryHelper.build(query);
        return this.remove(queryWrapper);
    }

    public <T, R> boolean deleteByEntity(T t, Func1<T, R>... funcs) {
        CopyOptions copyOptions = CopyOptions.create()
                .ignoreNullValue()
                .setIgnoreProperties(funcs)
                .setFieldNameEditor((key) -> CharSequenceUtil.toUnderlineCase(key));
        Map<String, Object> target = new HashMap<>();
        BeanUtil.beanToMap(t, target, copyOptions);
        return this.removeByMap(target);
    }

    public <P> int saveBySqlPlus(String sql, P params) {
        if (params instanceof Map) {
            return this.getBaseMapper().insertBySqlPlus(sql, (Map) params);
        } else {
            return this.getBaseMapper().insertBySqlPlus(sql, BeanUtil.beanToMap(params));
        }
    }

    public boolean saveFewSql(Collection<T> entityList, int batchSize) {
        return entityList.size() == this.getBaseMapper().saveFewSql(entityList, batchSize);
    }

    public boolean updateFewSql(Collection<T> entityList, int batchSize) {
        return entityList.size() == this.getBaseMapper().updateFewSql(entityList, batchSize);
    }
}
